import * as d3		from "d3";
import {Transform}	from "@/components/graph/utils/transform";
import {Lang}		from "@/components/graph/utils/lang";

export class WH {
    height: number;
    width: number;
}

export class Bounds extends WH {

    x: number;
    y: number;

	constructor(el:d3.Selection<SVGGElement, any, Element, any>|HTMLElement|SVGGraphicsElement) {
		super();
		if (el instanceof HTMLElement) {
			const bounds:DOMRect = el.getBoundingClientRect();	//absolute-position
			const selection = d3.select(el);
			this.x	= bounds.x + Lang.toNumber(selection.style("padding-left"),0);
			this.y	= bounds.y + Lang.toNumber(selection.style("padding-top"),0);
			this.width	= bounds.right - Lang.toNumber(selection.style("padding-right"),0) - this.x;
			this.height	= bounds.bottom - Lang.toNumber(selection.style("padding-bottom"),0) - this.y;
		} else {
			const node:SVGGraphicsElement = (el instanceof SVGGraphicsElement ? el: el.node() as SVGGraphicsElement);
			const bounds:DOMRect = node.getBBox();	//relative-position
			const transform:Transform = Transform.instance(node)
			this.x	= transform.x;
			this.y	= transform.y;
			this.width	= bounds.width;
			this.height	= bounds.height;
		}

	}

}

export class UI {



	public static align(el:SVGElement, horizontal:"none"|"left"|"right"|"center", vertical:"none"|"top"|"bottom"|"middle",  margin?: {left?:number, top?:number, right?:number,bottom?:number}) { 

		if(!el || !el.parentElement) return
		const outBounds:DOMRect = el.parentElement.getBoundingClientRect();
		const bounds:DOMRect = el.getBoundingClientRect();

		let X:number=0, Y:number=0;
		if(!margin) margin = {left:0, top:0, right:0, bottom:0}

		if(horizontal == "right") {
			X = outBounds.width - bounds.width - ( margin.right ? margin.right: 0);
		} else if(horizontal == "center") {
			X = (outBounds.width - bounds.width - (margin.right ? margin.right: 0))/2;

		} else {
			X = (margin && margin.left) ? margin.left: 0;
		}

		if(vertical == "bottom") {
			Y = outBounds.height - bounds.height - (margin.bottom ? margin.bottom: 0) ;
		} else if(vertical == "middle") {
			Y = (outBounds.height - bounds.height - (margin.bottom ? margin.bottom: 0))/2;
		} else {
			Y = margin.top ? margin.top: 0;
		}

		const k:number = Transform.instance(el.parentElement).k;	//calcuate parent elements's tranfrom-scale (ratio) 
		Transform.instance(el).translate(X/k,Y/k)
	}

	public static appendScrollableLayer(X:number, Y:number, bounds:Bounds, parentEl:d3.Selection<SVGGElement,any,SVGElement,any>, func: (selection: d3.Selection<SVGGElement, any, SVGElement, any>, ...args: any) => void , ...args: any[]) {

		const margin:number = 10;

		let scrollEl:d3.Selection<SVGForeignObjectElement,any,SVGElement,any> = parentEl.append("foreignObject")
			.attr("x",X)
			.attr("y",Y)
			.html(`<div xmlns="http://www.w3.org/1999/xhtml" style="height: 100%;padding:${margin}px;"></div>`)
		let svg:d3.Selection<SVGSVGElement,any,SVGElement,any> = scrollEl.select("div").append("svg")
		

		let outlineEl:d3.Selection<SVGGElement,any,SVGElement,any> = svg.append("g").attr("class","outline")

		outlineEl.call(func, args);


		const rect:DOMRect = outlineEl.node()!.getBoundingClientRect();

		outlineEl.insert("rect", "g.group:first-child")
			.attr("class", "background")
			.attr("width", rect.width)
			.attr("height", rect.height)

		scrollEl.attr("width", rect.width + (margin*2));
		svg.attr("width", rect.width + (margin*2))
		

		const outH:number = bounds.height - (Y+margin)-margin;
		const inH:number = outlineEl!.node()!.getBoundingClientRect().height + (margin*2);

		scrollEl.attr("height", (inH>outH) ? outH:inH);
		if(inH>outH) scrollEl.select("div").style("overflow-y", "scroll");
		svg.attr("height", inH );

	}


	public static ellipsisText(el:SVGTextElement, width:number): number {
		//if(el.x.baseVal.length > 0) width -= el.x.baseVal[0].value;
		if(el.getComputedTextLength() > width) {
			const text = `${el.textContent}`;
			const chars = text.split("");
			let len:number = chars.length - 3;
			while(len > 1) {
				len--;
				el.textContent = `${text.substring(0, len)}...`
				if(el.getComputedTextLength() < width) break;
			}
		}
		return el.getComputedTextLength();
	}

	public static appendBox(parentEl:d3.Selection<SVGGElement, any, SVGElement,any>, 
		render: (selection: d3.Selection<SVGGElement, any, SVGElement, any>, ...args: any[]) => void, 
		width?:number,
		padding?:{top:number,left:number, right:number, bottom:number},
		bg?:{fill:string, opacity?:number},
		border?:{width:number, color?:string, dash?:string}): d3.Selection<SVGGElement, any, SVGElement,any> {

		const boxWrap = parentEl.append("g").attr("class","boxWrap");

		//rendering
		const box = boxWrap.append("g").attr("class","box");
		if(render) box.call(render)

		// after rendering > calcuate border(background) bounds
		const bounds:DOMRect = boxWrap.node()!.getBBox();
		const x:number = -(border?border.width: 0);
		const y:number = bounds.y;
		const bottom:number = y + bounds.height + (padding?padding.top+padding.bottom:0);
		const right:number = x + (width?width:bounds.width) + (border?border.width*2: 0);	//add border-width

		// box (insert before g.box)
		const background = boxWrap.insert("path","g.box")
			.attr("class","background")
			.attr("d",`M${x},${y} L${right},${y} L${right},${bottom} L${x},${bottom} L${x},${y}`)

		if(bg) {
			background.attr("fill",bg.fill)
			if(bg.fill != "none" && bg.opacity) background.attr("fill-opacity",bg.opacity)
		}

		if(border) {
			background.attr("stroke","black").attr("stroke-width",border.width)
			if (border.color)  background.attr("stroke", border.color)
			if (border.dash) background.attr("stroke-dasharray", border.dash)
		}

		Transform.instance(box.node()!).translate(padding?padding.left+(border?border.width:0):0,padding?padding.top+(border?border.width:0):0);
		return box

	}

}
